/************************************
 *                                  *
 * Demonstration program "Puzzle"   *
 *                                  *
 * ported to GnuWin32: 24.1.97, kre *
 *                                  *
 ************************************/

/* this program does not run under Windows NT when compiled and linked with GCC!! :-( */
 
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#include "puzzle.h"

/**********************
 * global definitions *
 **********************/

#define PSIZE 4
#define TSIZE 50
#define NRTILES (PSIZE*PSIZE)
#define XCOORD(n) ((n)%PSIZE)
#define YCOORD(n) ((n)/PSIZE)

static HWND hWndMain;
static HBITMAP hPuzzle;
static HINSTANCE hInst;
static int gameTime;
static char szWndClassName[]="CPuzzle";

static int tiles[PSIZE][PSIZE];
static int emptyX,emptyY;

/*********************************
 * about dialog window procedure *
 *********************************/

int CALLBACK AboutDlgProc (HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)
{
   switch (message)
   {
      case WM_INITDIALOG:            /* initialize dialog */
         return 1;

      case WM_COMMAND:
         if (wParam==IDD_ABOUTOK ||  /* selected "OK" ? */
             wParam==IDCANCEL)       /* pressed ESCAPE ? */
         {
            EndDialog(hDlg,0);       /* close dialog */
            return 1;
         }
         break;

      case WM_DESTROY:
         EndDialog(hDlg,0);
         return 1;
    }
    return 0;                        /* run default event handler */
}

/****************
 * about dialog *
 ****************/

void About (void)
{
   DialogBox(hInst,MAKEINTRESOURCE(IDD_ABOUT),hWndMain,AboutDlgProc);
}

/************
 * new game *
 ************/

void NewGame (void)
{
   int i,next;
   int empty[NRTILES];

   for (i=0;i<NRTILES;i++)
      empty[i]=i;

   /* initialize tiles */
   memset(tiles,0,sizeof(tiles));
   for (i=0;i<NRTILES;i++) {
      next=rand()%(NRTILES-i);
      tiles[XCOORD(i)][YCOORD(i)]=empty[next];
      if (empty[next]==NRTILES-1) {
         emptyX=XCOORD(i);
         emptyY=YCOORD(i);
      }
      empty[next]=empty[NRTILES-i-1];
   }

   /* reset time counter */
   gameTime=0;

   /* redraw window */
   InvalidateRect(hWndMain,0,FALSE);
   UpdateWindow(hWndMain);
}

/******************************
 * a tile has been clicked at *
 ******************************/

void MoveTile (int x,int y)
{
   /* tile is beneath empty field? */
   if (abs(emptyX-x)+abs(emptyY-y)==1) {
      /* yes -> swap fields */
      int swap=tiles[x][y];
      tiles[x][y]=tiles[emptyX][emptyY];
      tiles[emptyX][emptyY]=swap;
      emptyX=x;
      emptyY=y;

      /* increase game counter */
      gameTime++;

      /* redraw window */
      InvalidateRect(hWndMain,NULL,0);
      UpdateWindow(hWndMain);
   }
}

/*************************
 * main window procedure *
 *************************/

LRESULT CALLBACK WndProc (HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
   switch (message)
   {
      case WM_PAINT:
      {
         HDC hdc;
         PAINTSTRUCT ps;

         hdc=BeginPaint(hWnd,&ps);

         if (hPuzzle) {
            HDC memdc;
            HBITMAP oldbmp;
            RECT rect;
            int x,y;
            char text[10];

            memdc=CreateCompatibleDC(hdc);
            oldbmp=SelectObject(memdc,hPuzzle);

            /* draw game time */
            sprintf(text,"Puzzle - %d moves",gameTime);
            SetWindowText(hWndMain,text);

            /* redraw tiles */
            for (x=0;x<PSIZE;x++)
               for (y=0;y<PSIZE;y++)
                   BitBlt(hdc,x*(TSIZE-1),y*(TSIZE-1),TSIZE,TSIZE,
                   memdc,XCOORD(tiles[x][y])*(TSIZE-1),YCOORD(tiles[x][y])*(TSIZE-1),SRCCOPY);

            SelectObject(memdc,oldbmp);
            DeleteDC(memdc);
         }

         EndPaint(hWnd,&ps);
         return 0;
      }

      case WM_COMMAND:

         switch (wParam) {
         case IDM_GAMENEW:   NewGame(); break;
         case IDM_GAMEEXIT:  PostQuitMessage(0); break;
         case IDM_HELPABOUT: About(); break;
         } 
         break;

      case WM_LBUTTONDOWN:
         MoveTile(LOWORD(lParam)/(TSIZE-1),HIWORD(lParam)/(TSIZE-1));
         break;

      case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }
   }
   return DefWindowProc(hWnd,message,wParam,lParam);
}

/****************************************
 * initialize first instance of program *
 ****************************************/

int InitApplication (HINSTANCE hInstCurr)
{
   WNDCLASS wc;   /* window class */

   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = (WNDPROC)WndProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = hInstCurr;
   wc.hIcon         = LoadIcon(hInstCurr,MAKEINTRESOURCE(IDI_MAINICON));
   wc.hCursor       = LoadCursor(0,IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
   wc.lpszMenuName  = MAKEINTRESOURCE(IDM_MAINMENU);
   wc.lpszClassName = szWndClassName;
   return RegisterClass(&wc);
}

/*************************************
 * initialization for every instance *
 *************************************/

int InitInstance (HINSTANCE hInstCurr,int nCmdShow)
{
   srand(clock());

   hWndMain=CreateWindow(szWndClassName,
      "Puzzle",
      WS_DLGFRAME | WS_SYSMENU,
      CW_USEDEFAULT,
      CW_USEDEFAULT,
      PSIZE*(TSIZE-1)+1+2*GetSystemMetrics(SM_CXDLGFRAME),
      PSIZE*(TSIZE-1)+1+GetSystemMetrics(SM_CYCAPTION)+GetSystemMetrics(SM_CYMENU)+2*GetSystemMetrics(SM_CYDLGFRAME),
      NULL,
      NULL,
      hInstCurr,
      NULL);

   if (hWndMain)
   {
      ShowWindow(hWndMain, nCmdShow);
      NewGame();
      return (TRUE);
   }
   return FALSE;
}

/****************
 * main program *
 ****************/

int WinMain (HINSTANCE hInstCurr,HINSTANCE hInstPrev,LPSTR lpszCmd,int nCmdShow)
{
   MSG msg;
   HACCEL hAccel;

   hInst=hInstCurr;
   hPuzzle=LoadBitmap(hInst,MAKEINTRESOURCE(IDB_PUZZLE));
   hAccel=LoadAccelerators(hInst,MAKEINTRESOURCE(IDA_SHORTCUTS));

   if (!hAccel || !hPuzzle) return 0;

   if (!hInstPrev)
      if (!InitApplication(hInstCurr)) return 0;

   if (!InitInstance(hInstCurr,nCmdShow)) return 0;

   while (GetMessage(&msg,NULL,0,0))
   {
      if (!TranslateAccelerator(hWndMain,hAccel,&msg)) {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }
   return msg.wParam ;
}
